package com.syj.qdp.framework.web.config;

import com.syj.qdp.framework.web.handler.GlobalResponseBodyHandler;
import com.syj.qdp.framework.common.enums.WebFilterOrderEnum;
import com.syj.qdp.framework.apilog.core.service.ApiErrorLogFrameworkService;
import com.syj.qdp.framework.web.core.cache.CacheRequestFilter;
import com.syj.qdp.framework.web.core.xss.XssRequestFilter;
import com.syj.qdp.framework.web.handler.GlobalExceptionHandler;
import com.syj.qdp.framework.web.util.FilterUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.ConditionalOnMissingFilterBean;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Controller;
import org.springframework.util.PathMatcher;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;

/**
 * Web 过滤器自动配置
 *
 * @author Lyon
 */
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Configuration
@EnableConfigurationProperties({
        XssProperties.class,
        WebProperties.class
})
@AutoConfigureBefore(WebMvcAutoConfiguration.class)
public class LyonWebAutoConfiguration extends WebMvcConfigurationSupport {

    @Resource
    WebProperties webProperties;

    @Value("spring.application.name")
    String applicationName;

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        /** 配置knife4j 显示文档 */
        registry.addResourceHandler("doc.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("service-worker.js").addResourceLocations("classpath:/META-INF/resources/");

        /**
         * 配置swagger-ui显示文档
         */
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        /** 公共部分内容 */
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        // 设置 API 前缀，仅仅匹配 controller 包下的
        configurer.addPathPrefix(webProperties.getApiPrefix(), clazz ->
                clazz.getPackage().getName().startsWith(webProperties.getControllerPackage()) &&
                        (clazz.isAnnotationPresent(RestController.class) ||
                                clazz.isAnnotationPresent(Controller.class))
        );
    }

    /***
     * 全局处理器
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(ApiErrorLogFrameworkService.class)
    public GlobalExceptionHandler globalExceptionHandler(ApiErrorLogFrameworkService apiErrorLogFrameworkService) {
        return new GlobalExceptionHandler(applicationName, apiErrorLogFrameworkService);
    }

    @Bean
    @ConditionalOnMissingBean
    public GlobalResponseBodyHandler globalResponseBodyHandler() {
        return new GlobalResponseBodyHandler();
    }


    /***
     * WEB 配置
     */

    /**
     * 可重复读取body Filter配置
     *
     * @return
     */
    @Bean
    @ConditionalOnMissingFilterBean(CacheRequestFilter.class)
    FilterRegistrationBean<CacheRequestFilter> cacheFilterBean() {
        return FilterUtils.createFilterRegistration(new CacheRequestFilter(), WebFilterOrderEnum.REQUEST_BODY_CACHE_FILTER);
    }

    /**
     * Xss 防止注入Filter配置
     *
     * @param xssProperties xxs属性
     * @param pathMatcher   路径匹配器
     * @return
     */
    @Bean
    @ConditionalOnMissingFilterBean(XssRequestFilter.class)
    public FilterRegistrationBean<XssRequestFilter> xssFilterBean(XssProperties xssProperties, PathMatcher pathMatcher) {
        return FilterUtils.createFilterRegistration(new XssRequestFilter(xssProperties, pathMatcher), WebFilterOrderEnum.XSS_FILTER);
    }

    @Bean
    @ConditionalOnMissingFilterBean(CorsFilter.class)
    public FilterRegistrationBean<CorsFilter> corsFilterBean() {
        UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowCredentials(true);
        /** 设置访问源请求方法*/
        corsConfiguration.addAllowedMethod("*");
        /** 设置访问源请求方法*/
        corsConfiguration.addAllowedHeader("*");
        /**设置访问源请求地址*/
        corsConfiguration.addAllowedOrigin("*");
        corsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
        return FilterUtils.createFilterRegistration(new CorsFilter(corsConfigurationSource), WebFilterOrderEnum.CORS_FILTER);
    }

}
